﻿using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using System.Linq;
using Moq;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using SmarterCharger.Library.Tests.Unit.Infrastructure;
using SmarterCharger.Library.Repositories.Interfaces;
using SmarterCharger.Library.Model;
using SmarterCharger.Library.Services.Interfaces;
using SmarterCharger.Library.Services;
using SmarterCharger.Library.Infrastructure;

namespace SmarterCharger.Library.Tests.Unit.SmartPlugLoaderSpecifications
{
    public abstract class WhenLoad : TestBaseAsync<LoadSmartPlugResult>
    {
        protected ISmartPlugLoader SmartPlugLoader { get; private set; }
        private Mock<IEnergyDeviceRepository> EnergyDeviceRepository { get; set; }
        protected virtual HttpStatusCode StatusCode { get { return HttpStatusCode.OK; } }

        protected virtual IEnumerable<EnergyDevice> EnergyDevices { get { return new List<EnergyDevice>(); } }

        protected virtual string Username { get { return "dummy-username"; } }
        protected virtual string Password { get { return "dummy-password"; } }

        protected override void Arrange()
        {
            EnergyDeviceRepository = new Mock<IEnergyDeviceRepository>();
            EnergyDeviceRepository.Setup(r => r.GetEnergyDevices(Username, Password))
                .Returns(Task.FromResult<HttpResponseResult<IEnumerable<EnergyDevice>>>(new HttpResponseResult<IEnumerable<EnergyDevice>> { StatusCode = StatusCode, Result = EnergyDevices }));

            SmartPlugLoader = new SmartPlugLoader(EnergyDeviceRepository.Object);
        }

        protected override Task<LoadSmartPlugResult> Act()
        {
            return SmartPlugLoader.Load(Username, Password);
        }
    }

    [TestClass]
    public class WhenLoadGivenUsernameIsEmpty : WhenLoad
    {
        protected override string Username
        {
            get
            {
                return string.Empty;
            }
        }

        [TestMethod]
        public void ThenAnArgumentNullExceptionShouldBeThrown()
        {
            var argumentNullException = BaseException as ArgumentNullException;
            Assert.IsNotNull(argumentNullException);
            Assert.AreEqual("username", argumentNullException.ParamName);
        }
    }

    [TestClass]
    public class WhenLoadGivenPasswordIsEmpty : WhenLoad
    {
        protected override string Password
        {
            get
            {
                return string.Empty;
            }
        }

        [TestMethod]
        public void ThenAnArgumentNullExceptionShouldBeThrown()
        {
            var argumentNullException = BaseException as ArgumentNullException;
            Assert.IsNotNull(argumentNullException);
            Assert.AreEqual("password", argumentNullException.ParamName);
        }
    }

    [TestClass]
    public class WhenLoadGivenEnergyDevicesContainsOneNikoPlug : WhenLoad
    {
        private EnergyDevice _nikoPlug;

        protected override IEnumerable<EnergyDevice> EnergyDevices
        {
            get
            {
                _nikoPlug = new EnergyDevice { HWModel = "niko", EnergyDeviceId = Guid.NewGuid(), Name = "dummy-name", SerialNumber = "dummy-serial" };

                return new List<EnergyDevice> { _nikoPlug };
            }
        }

        [TestMethod]
        public void ThenResultShouldContainOneNikoSmartPlug()
        {
            Assert.AreEqual(1, Result.SmartPlugs.Count());
            Assert.AreEqual(_nikoPlug.EnergyDeviceId, Result.SmartPlugs.Single().EnergyDeviceId);
            Assert.AreEqual(_nikoPlug.Name, Result.SmartPlugs.Single().Name);
            Assert.AreEqual(_nikoPlug.SerialNumber, Result.SmartPlugs.Single().SerialNumber);
        }
    }

    [TestClass]
    public class WhenLoadGivenGetEnergyDevicesIsUnauthorized : WhenLoad
    {
        protected override HttpStatusCode StatusCode
        {
            get
            {
                return HttpStatusCode.Unauthorized;
            }
        }
        
        [TestMethod]
        public void ThenResultShouldBeMarkedAsCredentialsInvalid()
        {
            Assert.IsFalse(Result.IsCredentialsValid);
        }
    }

    [TestClass]
    public class WhenLoadGivenGetEnergyDevicesTimesOut : WhenLoad
    {
        protected override HttpStatusCode StatusCode
        {
            get
            {
                return HttpStatusCode.RequestTimeout;
            }
        }

        [TestMethod]
        public void ThenAnExceptionShouldBeThrown()
        {
            Assert.IsNotNull(BaseException);

            var expectedErrorMessage = string.Format("Failed to load smartplugs, did not expect '{0}'. This could be a temporary error, please try again.", 
                        StatusCode.ToString());

            Assert.AreEqual(expectedErrorMessage, BaseException.Message);
        }
    }
}
